# 节点图形项（继承QGraphicsItem）
from PyQt5.QtWidgets import QGraphicsRectItem, QGraphicsEllipseItem, QGraphicsTextItem
from PyQt5.QtCore import Qt, QRectF, QPointF, pyqtSignal
from PyQt5.QtGui import QColor, QBrush, QPen, QPainterPath


class Port(QGraphicsEllipseItem):
    """
    表示节点的端口，继承自QGraphicsEllipseItem。
    端口用于连接不同节点，分为输入端口和输出端口。
    """
    def __init__(self, parent, pos, is_output=False):
        """
        初始化端口对象。

        :param parent: 父节点对象
        :param pos: 端口在场景中的位置
        :param is_output: 指示该端口是否为输出端口，默认为False
        """
        super().__init__(-6, -6, 12, 12, parent)
        self.parent_node = parent
        self.is_output = is_output

        # 端口样式
        self.setBrush(QBrush(Qt.darkGray))  # 设置端口的填充颜色为深灰色
        self.setPen(QPen(Qt.black, 1))  # 设置端口的边框颜色为黑色，宽度为1
        self.setPos(pos)  # 设置端口的位置

        # 交互设置
        self.setAcceptHoverEvents(True)  # 允许端口接收悬停事件

    def hoverEnterEvent(self, event):
        """
        处理鼠标悬停进入端口的事件。
        当鼠标悬停在端口上时，将端口的填充颜色设置为橙色。

        :param event: 鼠标事件对象
        """
        self.setBrush(QBrush(QColor(255, 140, 0)))  # 悬停时橙色
        super().hoverEnterEvent(event)

    def hoverLeaveEvent(self, event):
        """
        处理鼠标悬停离开端口的事件。
        当鼠标离开端口时，将端口的填充颜色恢复为深灰色。

        :param event: 鼠标事件对象
        """
        self.setBrush(QBrush(Qt.darkGray))
        super().hoverLeaveEvent(event)

class Node(QGraphicsRectItem):
    """
    表示一个节点，继承自QGraphicsRectItem。
    节点包含标题、输入端口和输出端口，并且可以在场景中移动和选择。
    """
    positionChanged = pyqtSignal()  # 定义一个信号，当节点位置改变时发出

    def __init__(self, title, width=120, height=60):
        """
        初始化节点对象。

        :param title: 节点的标题
        :param width: 节点的宽度，默认为120
        :param height: 节点的高度，默认为60
        """
        super().__init__(0, 0, width, height)
        self.title = title
        self.ports = []

        # 初始化样式和交互
        self.init_ui()  # 初始化节点的UI样式和交互属性
        self.create_ports()  # 创建节点的输入和输出端口

    def init_ui(self):
        """
        初始化节点的UI样式和交互属性。
        设置节点可移动、可选择，并发送几何变化信号。
        配置节点的颜色和标题文字。
        """
        self.setFlag(QGraphicsRectItem.ItemIsMovable, True)  # 允许节点在场景中移动
        self.setFlag(QGraphicsRectItem.ItemIsSelectable, True)  # 允许节点被选择
        self.setFlag(QGraphicsRectItem.ItemSendsGeometryChanges, True)  # 节点几何变化时发送信号

        # 颜色配置
        self.setBrush(QBrush(QColor(200, 220, 255)))  # 设置节点的填充颜色
        self.setPen(QPen(Qt.darkBlue, 2))  # 设置节点的边框颜色和宽度

        # 标题文字
        self.text = QGraphicsTextItem(self.title, self)  # 创建标题文字项
        text_width = self.text.boundingRect().width()  # 获取标题文字的宽度
        self.text.setPos((self.rect().width() - text_width) / 2, 8)  # 设置标题文字的位置

    def create_ports(self):
        """
        创建节点的输入和输出端口。
        在节点的左侧创建一个输入端口，右侧创建一个输出端口。
        """
        # 输入端口（左侧）
        input_port = Port(self, QPointF(0, self.rect().height() / 2))  # 创建输入端口
        self.ports.append(input_port)  # 将输入端口添加到端口列表中

        # 输出端口（右侧）
        output_port = Port(self, QPointF(self.rect().width(),
                                         self.rect().height() / 2),
                           is_output=True)  # 创建输出端口
        self.ports.append(output_port)  # 将输出端口添加到端口列表中

    def itemChange(self, change, value):
        """
        处理节点的属性变化事件。
        当节点的位置发生变化时，发出positionChanged信号。

        :param change: 变化的类型
        :param value: 变化后的值
        :return: 调用父类的itemChange方法返回结果
        """
        if change == QGraphicsRectItem.ItemPositionHasChanged:
            self.positionChanged.emit()  # 通知连接线更新
        return super().itemChange(change, value)

    def mouseMoveEvent(self, event):
        """
        处理鼠标移动事件。
        调用父类的鼠标移动事件处理方法，并可选择限制节点的移动范围。

        :param event: 鼠标事件对象
        """
        super().mouseMoveEvent(event)
        # 限制移动范围在场景内（可选）
        new_pos = self.pos()
        self.setPos(new_pos.x(), new_pos.y())

    def boundingRect(self):
        """
        返回节点的边界矩形。

        :return: 节点的边界矩形对象
        """
        return QRectF(0, 0, self.rect().width(), self.rect().height())

    def paint(self, painter, option, widget=None):
        """
        绘制节点的图形。
        绘制一个圆角矩形作为节点的外观。

        :param painter: 用于绘制的QPainter对象
        :param option: 绘制选项（未使用）
        :param widget: 绘制所在的部件（未使用）
        """
        # 绘制圆角矩形
        path = QPainterPath()
        path.addRoundedRect(self.rect(), 8, 8)  # 创建一个圆角矩形路径
        painter.setPen(self.pen())  # 设置绘制的画笔
        painter.setBrush(self.brush())  # 设置绘制的画刷
        painter.drawPath(path)

